Code Coverage |
||||||||||
Classes and Traits |
Functions and Methods |
Lines |
||||||||
| Total | |
0.00% |
0 / 1 |
|
70.00% |
7 / 10 |
CRAP | |
78.57% |
66 / 84 |
| ProductProcessor | |
0.00% |
0 / 1 |
|
70.00% |
7 / 10 |
43.72 | |
78.57% |
66 / 84 |
| __construct | |
100.00% |
1 / 1 |
1 | |
100.00% |
11 / 11 |
|||
| process | |
0.00% |
0 / 1 |
20.70 | |
76.60% |
36 / 47 |
|||
| filterIdenticalData | |
100.00% |
1 / 1 |
1 | |
100.00% |
1 / 1 |
|||
| getIdentifier | |
100.00% |
1 / 1 |
2 | |
100.00% |
1 / 1 |
|||
| getFamilyCode | |
0.00% |
0 / 1 |
4.18 | |
77.78% |
7 / 9 |
|||
| filterItemData | |
100.00% |
1 / 1 |
2 | |
100.00% |
5 / 5 |
|||
| updateProduct | |
100.00% |
1 / 1 |
1 | |
100.00% |
2 / 2 |
|||
| validateProduct | |
100.00% |
1 / 1 |
1 | |
100.00% |
1 / 1 |
|||
| detachProduct | |
100.00% |
1 / 1 |
1 | |
100.00% |
2 / 2 |
|||
| skipItemAndReturnException | |
0.00% |
0 / 1 |
12.00 | |
0.00% |
0 / 5 |
|||
| <?php | |
| namespace Akeneo\Pim\Enrichment\Component\Product\Connector\Processor\Denormalizer; | |
| use Akeneo\Pim\Enrichment\Component\Product\Comparator\Filter\FilterInterface; | |
| use Akeneo\Pim\Enrichment\Component\Product\EntityWithFamilyVariant\AddParent; | |
| use Akeneo\Pim\Enrichment\Component\Product\Model\ProductInterface; | |
| use Akeneo\Pim\Enrichment\Component\Product\ProductModel\Filter\AttributeFilterInterface; | |
| use Akeneo\Tool\Component\Batch\Item\FileInvalidItem; | |
| use Akeneo\Tool\Component\Batch\Item\InvalidItemException; | |
| use Akeneo\Tool\Component\Batch\Item\ItemProcessorInterface; | |
| use Akeneo\Tool\Component\Batch\Step\StepExecutionAwareInterface; | |
| use Akeneo\Tool\Component\Connector\Processor\Denormalization\AbstractProcessor; | |
| use Akeneo\Tool\Component\StorageUtils\Detacher\ObjectDetacherInterface; | |
| use Akeneo\Tool\Component\StorageUtils\Exception\InvalidPropertyException; | |
| use Akeneo\Tool\Component\StorageUtils\Exception\PropertyException; | |
| use Akeneo\Tool\Component\StorageUtils\Repository\IdentifiableObjectRepositoryInterface; | |
| use Akeneo\Tool\Component\StorageUtils\Updater\ObjectUpdaterInterface; | |
| use Symfony\Component\Security\Core\Exception\AccessDeniedException; | |
| use Symfony\Component\Security\Core\Exception\InvalidArgumentException; | |
| use Symfony\Component\Validator\ConstraintViolationListInterface; | |
| use Symfony\Component\Validator\Validator\ValidatorInterface; | |
| /** | |
| * Product import processor, allows to, | |
| * - create / update | |
| * - convert localized attributes | |
| * - validate | |
| * - skip invalid ones and detach it | |
| * - return the valid ones | |
| * | |
| * @author Julien Sanchez <julien@akeneo.com> | |
| * @copyright 2015 Akeneo SAS (http://www.akeneo.com) | |
| * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0) | |
| */ | |
| class ProductProcessor extends AbstractProcessor implements ItemProcessorInterface, StepExecutionAwareInterface | |
| { | |
| /** @var FindProductToImport */ | |
| private $findProductToImport; | |
| /** @var AddParent */ | |
| private $addParent; | |
| /** @var ObjectUpdaterInterface */ | |
| protected $updater; | |
| /** @var ValidatorInterface */ | |
| protected $validator; | |
| /** @var ObjectDetacherInterface */ | |
| protected $detacher; | |
| /** @var FilterInterface */ | |
| protected $productFilter; | |
| /** @var AttributeFilterInterface */ | |
| private $productAttributeFilter; | |
| /** @var MediaStorer */ | |
| private $mediaStorer; | |
| public function __construct( | |
| IdentifiableObjectRepositoryInterface $repository, | |
| FindProductToImport $findProductToImport, | |
| AddParent $addParent, | |
| ObjectUpdaterInterface $updater, | |
| ValidatorInterface $validator, | |
| ObjectDetacherInterface $detacher, | |
| FilterInterface $productFilter, | |
| AttributeFilterInterface $productAttributeFilter, | |
| MediaStorer $mediaStorer | |
| ) { | |
| parent::__construct($repository); | |
| $this->findProductToImport = $findProductToImport; | |
| $this->addParent = $addParent; | |
| $this->updater = $updater; | |
| $this->validator = $validator; | |
| $this->detacher = $detacher; | |
| $this->productFilter = $productFilter; | |
| $this->productAttributeFilter = $productAttributeFilter; | |
| $this->repository = $repository; | |
| $this->mediaStorer = $mediaStorer; | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public function process($item) | |
| { | |
| $itemHasStatus = isset($item['enabled']); | |
| if (!isset($item['enabled'])) { | |
| $item['enabled'] = $jobParameters = $this->stepExecution->getJobParameters()->get('enabled'); | |
| } | |
| $identifier = $this->getIdentifier($item); | |
| if (null === $identifier) { | |
| $this->skipItemWithMessage($item, 'The identifier must be filled'); | |
| } | |
| $parentProductModelCode = $item['parent'] ?? ''; | |
| try { | |
| $familyCode = $this->getFamilyCode($item); | |
| $item['family'] = $familyCode; | |
| $item = $this->productAttributeFilter->filter($item); | |
| $filteredItem = $this->filterItemData($item); | |
| $product = $this->findProductToImport->fromFlatData($identifier, $familyCode); | |
| } catch (AccessDeniedException $e) { | |
| throw $this->skipItemAndReturnException($item, $e->getMessage(), $e); | |
| } | |
| if (false === $itemHasStatus && null !== $product->getId()) { | |
| unset($filteredItem['enabled']); | |
| } | |
| $jobParameters = $this->stepExecution->getJobParameters(); | |
| $enabledComparison = $jobParameters->get('enabledComparison'); | |
| if ($enabledComparison) { | |
| $filteredItem = $this->filterIdenticalData($product, $filteredItem); | |
| if (empty($filteredItem) && null !== $product->getId()) { | |
| $this->detachProduct($product); | |
| $this->stepExecution->incrementSummaryInfo('product_skipped_no_diff'); | |
| return null; | |
| } | |
| } | |
| if ('' !== $parentProductModelCode && !$product->isVariant()) { | |
| try { | |
| $product = $this->addParent->to($product, $parentProductModelCode); | |
| } catch (\InvalidArgumentException $e) { | |
| $this->detachProduct($product); | |
| $this->skipItemWithMessage($item, $e->getMessage(), $e); | |
| } | |
| } | |
| if (isset($filteredItem['values'])) { | |
| try { | |
| $filteredItem['values'] = $this->mediaStorer->store($filteredItem['values']); | |
| } catch (InvalidPropertyException $e) { | |
| $this->detachProduct($product); | |
| $this->skipItemWithMessage($item, $e->getMessage(), $e); | |
| } | |
| } | |
| try { | |
| $this->updateProduct($product, $filteredItem); | |
| } catch (PropertyException $exception) { | |
| $this->detachProduct($product); | |
| $message = sprintf('%s: %s', $exception->getPropertyName(), $exception->getMessage()); | |
| $this->skipItemWithMessage($item, $message, $exception); | |
| } catch (InvalidArgumentException | AccessDeniedException $exception) { | |
| $this->detachProduct($product); | |
| $this->skipItemWithMessage($item, $exception->getMessage(), $exception); | |
| } | |
| $violations = $this->validateProduct($product); | |
| if ($violations->count() > 0) { | |
| $this->detachProduct($product); | |
| $this->skipItemWithConstraintViolations($item, $violations); | |
| } | |
| return $product; | |
| } | |
| /** | |
| * @param ProductInterface $product | |
| * @param array $filteredItem | |
| * | |
| * @return array | |
| */ | |
| protected function filterIdenticalData(ProductInterface $product, array $filteredItem) | |
| { | |
| return $this->productFilter->filter($product, $filteredItem); | |
| } | |
| /** | |
| * @param array $item | |
| * | |
| * @return string|null | |
| */ | |
| protected function getIdentifier(array $item) | |
| { | |
| return isset($item['identifier']) ? $item['identifier'] : null; | |
| } | |
| /** | |
| * @param array $item | |
| * | |
| * @return string | |
| */ | |
| protected function getFamilyCode(array $item): string | |
| { | |
| if (key_exists('family', $item)) { | |
| return $item['family']; | |
| } | |
| $product = $this->repository->findOneByIdentifier($item['identifier']); | |
| if (null === $product) { | |
| return ''; | |
| } | |
| $family = $product->getFamily(); | |
| if (null === $family) { | |
| return ''; | |
| } | |
| return $family->getCode(); | |
| } | |
| /** | |
| * Filters item data to remove associations which are imported through a dedicated processor because we need to | |
| * create any products before to associate them | |
| * | |
| * @param array $item | |
| * | |
| * @return array | |
| */ | |
| protected function filterItemData(array $item) | |
| { | |
| foreach ($this->repository->getIdentifierProperties() as $identifierProperty) { | |
| unset($item['values'][$identifierProperty]); | |
| } | |
| unset($item['identifier']); | |
| unset($item['associations']); | |
| return $item; | |
| } | |
| /** | |
| * @param ProductInterface $product | |
| * @param array $filteredItem | |
| * | |
| * @throws PropertyException | |
| */ | |
| protected function updateProduct(ProductInterface $product, array $filteredItem) | |
| { | |
| $this->updater->update($product, $filteredItem); | |
| } | |
| /** | |
| * @param ProductInterface $product | |
| * | |
| * @throws \InvalidArgumentException | |
| * | |
| * @return ConstraintViolationListInterface | |
| */ | |
| protected function validateProduct(ProductInterface $product) | |
| { | |
| return $this->validator->validate($product); | |
| } | |
| /** | |
| * Detaches the product from the unit of work is the responsibility of the writer but in this case we | |
| * want ensure that an updated and invalid product will not be used in the association processor | |
| * | |
| * @param ProductInterface $product | |
| */ | |
| protected function detachProduct(ProductInterface $product) | |
| { | |
| $this->detacher->detach($product); | |
| } | |
| private function skipItemAndReturnException(array $item, $message, \Exception $previousException = null): InvalidItemException | |
| { | |
| if ($this->stepExecution) { | |
| $this->stepExecution->incrementSummaryInfo('skip'); | |
| } | |
| $itemPosition = null !== $this->stepExecution ? $this->stepExecution->getSummaryInfo('item_position') : 0; | |
| $invalidItem = new FileInvalidItem($item, $itemPosition); | |
| return new InvalidItemException($message, $invalidItem, [], 0, $previousException); | |
| } | |
| } |